MCP (Model Context Protocol) tools loader.
This module provides async functions to load and manage MCP servers using
langchain-mcp-adapters, supporting Claude Desktop style JSON configs.
It also supports automatic discovery of .mcp.json files from user-level
and project-level locations.
Load states a configured MCP server can end up in.
ok means the server loaded successfully and has an authoritative tool list.
unauthenticated means the server requires OAuth login before tools can load.
error means the server failed to load after a connection or configuration
failure.
disabled is set when the user has turned the server off via the TUI
(/mcp -> F2). No connection is attempted and no tools are loaded, but
the entry is still surfaced in the viewer so the user can re-enable it.
awaiting_reconnect is a transient UI-only state used after OAuth login
has succeeded but before the LangGraph server has restarted and loaded
the newly available MCP tools.
Display strings for the auto-discovered MCP config paths.
Ordered from lowest to highest precedence. Each entry is (path, label)
suitable for rendering in help screens and error messages. The runtime
discovery in discover_mcp_configs builds the same paths from
Path.home() and _resolve_project_config_base().
Resolve ${VAR} references in one MCP server's supported fields.
Interpolates the command, url, args, env, and headers
fields (see _interpolate_env for the reference syntax); every other
field is copied through verbatim. The input is not mutated.
Load and validate MCP configuration from a JSON file.
Supports multiple server types:
command, args, env fields (default)type: "sse", url, and optional headerstype: "http", url, and optional headersAny server type may also set an optional tool filter:
allowedTools: list of tool names or patterns to keep (all others dropped)disabledTools: list of tool names or patterns to drop (all others kept)Entries are either literal tool names or fnmatch-style glob patterns
(entries containing *, ?, or [). Each entry is matched against both
the bare MCP tool name and the server-prefixed form
(f"{server_name}_{tool}"), so either read_* or fs_read_* works.
Setting both fields on a single server is an error.
Infer the project root that owns a project-level MCP config path.
Return only the project servers that survive the user's trust policy.
The single place the per-server trust rule lives, shared by the runtime
tool loader and the mcp login resolver so reject-precedence cannot drift
between them: a disabled name is dropped even from a config_trusted
config; otherwise a server is kept when the whole config is trusted or the
user's scoped approvals / env allowlist enable it (is_enabled).
Find MCP config files from standard locations.
Checks the paths listed in MCP_CONFIG_DISCOVERY_PATHS, lowest to
highest precedence.
Split discovered config paths into user-level and project-level configs.
Extract stdio server entries from a parsed MCP config.
Return a ProjectServerSummary for every server in a project config.
Used by the trust prompt and the untrusted-config skip warning so that
both stdio servers (which spawn local commands) and remote servers
(which can SSRF or exfiltrate environment variables via interpolated
headers when an attacker controls .mcp.json) are gated identically.
Merge multiple MCP config dicts by server name.
Load a single MCP config file, returning None on any error.
Disabled servers are removed before per-server validation, so explicitly
denied entries can neither block loading nor surface to a caller inspecting
the config. The single-file counterpart to load_merged_mcp_configs_lenient
(which the trust prompt uses); this one has no production caller today and is
retained as the standalone lenient loader.
Load and validate project configs after resolving precedence.
The trust prompt must inspect the exact merged server definitions that a whole-config approval can activate. Parsing each file with per-server validation first can discard valid lower-precedence siblings when a bad entry in that file is replaced by a valid higher-precedence definition.
Load an MCP config file, returning (config, error).
Missing files yield (None, None) — not an error. Malformed files
yield (None, error_text) so callers can surface the reason to users.
Load MCP tools from a configuration file.
Resolve MCP config and load tools.
Auto-discovers configs from standard locations and merges them. When
explicit_config_path is provided it is added as the highest-precedence
source and errors in that file are fatal.
User-level allow/deny lists for project MCP servers.
Sourced only from the user's own configuration — the home config.toml, the
global ~/.deepagents/.env, and shell-exported env — never from a repo, so a
committed .mcp.json cannot self-approve. Persisted approvals for fixed
remote URLs bind to one validated local Git repository. Local commands and
interpolated remote URLs bind to the exact resolved worktree. All include the
server definition's fingerprint. Env-sourced approvals remain explicit
process-wide name approvals.
The "reject wins" invariant — a name in both approval and rejection data is
only rejected — is enforced in __post_init__, so every instance is disjoint
no matter how it was constructed; callers need not pre-subtract.
Explicit user/project path context for project-sensitive behavior.
Metadata for a single MCP tool.
Metadata for a configured MCP server and its tools.
An MCP configuration file is malformed or structurally invalid.
Subclasses ValueError so existing except ValueError handlers
keep working; new code can catch this specifically to render a
user-actionable message (typically with a file path and hint).
Lazy, per-server cache of persistent MCP sessions.
Discovery always happens through throwaway sessions. Live sessions are only created on the first real tool call inside the runtime event loop so sessions stay bound to the loop that owns their subprocess/transport handles, and so stdio servers are not restarted on every invocation.
A project MCP server row shown to the user and gated for trust.
A NamedTuple (not a bare 3-tuple) so the three same-typed str slots get
field names — a name/kind swap can't type-check silently — while staying
tuple-compatible with existing unpacking and indexing.